#light
namespace Langexplr.Abc

open Langexplr.Abc
open System.IO

type ConstantPoolCreator() =
   class
   let mutable strings = [] : string list
   let mutable nsSets = [] : AbcNsSet list
   let mutable namespaces  = [] : AbcNamespace list
   let mutable multinames  = [] : MultiNameInfo list

   let mutable doubleLiterals : double list = []
   let mutable intLiterals :  int list = []
   let mutable uintLiterals :  uint32 list = []


   member this.GetIntIndex(value : int)  =
     match List.tryFindIndex (fun x -> x = value) intLiterals with
        | Some index -> (index+1)
        | None ->  intLiterals <- (List.append intLiterals [value]);
                   (List.length(intLiterals) )



   member this.GetDoubleIndex(value : double)  =
     match List.tryFindIndex (fun x -> x = value) doubleLiterals with
        | Some index -> (index+1)
        | None ->  doubleLiterals <- (List.append doubleLiterals [value]);
                   (List.length(doubleLiterals) )


   member this.GetUIntIndex(value : uint32)  =
     match List.tryFindIndex (fun x -> x = value) uintLiterals with
        | Some index -> (index+1)
        | None ->  uintLiterals <- (List.append uintLiterals [value]);
                   (List.length(uintLiterals) )


   member this.AddString(aString : string) =
     match List.tryFindIndex (fun x -> x = aString) strings with
        | Some index -> (index+1)
        | None ->  strings <- (List.append strings [aString]);
                   (List.length(strings) )


               
   member this.GetNamespace(namespaceName: string , nsKind : NamespaceKind) =     
      let nameIndex =  this.AddString(namespaceName) in
      match List.tryFindIndex (fun ({kind = kind ;nameIndex = index}) -> index = nameIndex && kind = nsKind) namespaces with
        | Some index -> (index+1)
        | None ->  namespaces <- (List.append namespaces [{kind = nsKind;nameIndex = nameIndex}]);
                   (List.length(namespaces) )
                      
   member this.GetNamespaceSet(namespaces : Ns array) =
      let nsIndexes = Array.map (fun (Ns(nsName,kind)) ->  this.GetNamespace(nsName,kind)) namespaces in
      match List.tryFindIndex (fun ({ count = _;ns = values}) -> values = nsIndexes) nsSets with
        | Some index -> (index+1)
        | None ->  nsSets <- (List.append nsSets [{count = nsIndexes.Length; ns = nsIndexes}]);
                   (List.length(nsSets))
      
      
   member this.GetNameEntry(name : QualifiedName) =
      match name with
         | MQualifiedName(namespaces,name) -> this.GetMultiname(namespaces,name)         
         | SQualifiedName "*" -> this.GetWildcardTypeIndex()
         | CQualifiedName(Ns(namespaceName,nsKind),name) -> 
             let newQName = QName(this.GetNamespace(namespaceName,nsKind),this.AddString(name))
             if not(List.exists (fun e -> e = newQName) multinames) then
                multinames <- (List.append multinames [newQName])
             else
                ()
             (List.findIndex (fun e -> e = newQName) multinames) + 1 //List.length(multinames)
         | SQualifiedName name -> 
              let name = QName(0,this.AddString(name)) in
                 this.AddMultiname(name)

         
   member this.GetMultiname(namespaces : Ns array, name : string) =
      let nameIndex = this.AddString(name)
      let nsSetIndex = this.GetNamespaceSet(namespaces)
      let newMultiname = Multiname(nsSetIndex,nameIndex)
      this.AddMultiname(newMultiname)

   member this.AddMultiname(newMultiname) =
      match List.tryFindIndex (fun (multiName : MultiNameInfo) -> multiName = newMultiname) multinames with
        | Some index -> (index+1)
        | None ->  multinames <- (List.append multinames [newMultiname]);
                   (List.length(multinames) )
                   
   member this.GetWildcardTypeIndex() = 0
                   
   member this.CreateConstantPool() =
       ConstantPoolInfo(
            List.toArray intLiterals,
            List.toArray uintLiterals,
            List.toArray doubleLiterals,
            List.toArray strings,//stringInfo : string array, 
            List.toArray namespaces,//namespaceInfo : AbcNamespace array, 
            List.toArray nsSets,//nsSetInfo : AbcNsSet array, 
            List.toArray multinames )//multiname : MultiNameInfo array) = 
end


type AbcFileCreator() = 
   class
     let cPoolCreator : ConstantPoolCreator = ConstantPoolCreator()
     let mutable methods : AbcMethodInfo list = []
     (*metadata : AbcMetadata array,
     instances : AbcInstanceInfo array,
     classes : AbcClassInfo array,*)
     let mutable scripts : AbcScriptInfo list = []
     let mutable methodBodies : AbcMethodBodyInfo list = []
     let mutable classes : AbcClassInfo list = []
     let mutable instances : AbcInstanceInfo list = []


     member this.GetClassIndexForNameIndex(index) = 
       (List.length instances) -
        (List.findIndex 
                 (fun (iInfo:AbcInstanceInfo) -> iInfo.name = index) 
                 instances)  - 1
     
     member this.ConstantPoolCreator 
        with get() = cPoolCreator     
 

     member this.GetClassIndexForName(name:QualifiedName) = 
       let nameIndex = cPoolCreator.GetNameEntry(name) in
       let classIndex =  List.tryFindIndex
                              (fun (iInfo:AbcInstanceInfo) -> iInfo.name = nameIndex)
                              instances in
       match classIndex with
       | Some(index) -> Some <| (List.length instances) - index - 1
       | None -> None
    
     member this.AddScript(aScript : AbcScriptInfo)  =
        scripts <- (aScript::scripts)
        List.length scripts
     member this.AddMethodInformation(aMethod : AbcMethodInfo)  =
        methods <- (aMethod::methods)
        (List.length methods) - 1
        
     member private this.AddMethodBody(aMethodBodyInfo : AbcMethodBodyInfo) =
        methodBodies <- (aMethodBodyInfo::methodBodies)
        List.length methodBodies
        
     member this.AddClass(classinstanceInfo:AbcInstanceInfo * AbcClassInfo) =
         let (instanceInformation,classInformation) =  classinstanceInfo
         classes <- (classInformation::classes) 
         instances <- (instanceInformation::instances) 
         List.length instances 
        
     member this.AddMethodInformationWithBody(aMethodWithBody : AbcMethodInfo * (AbcMethodBodyInfo Option))  =
        match aMethodWithBody with
          | (aMethod,Some body) ->
                 let _ = this.AddMethodBody(body.CloneWithMethodId( (List.length methods)))
                 methods <- (aMethod::methods)                 
          | (aMethod,_) -> 
                 methods <- (aMethod::methods)
        (List.length methods) - 1
        
     
    
     member this.CreateFile() =
        AbcFile(  uint16(16),
                  uint16(46),
                  cPoolCreator.CreateConstantPool() ,
                  methods |> List.rev |> List.toArray ,
                  [||],
                  instances |> List.rev |> List.toArray ,
                  classes |> List.rev |> List.toArray ,
                  scripts |> List.rev |> List.toArray ,
                  methodBodies |> List.rev |> List.toArray )
end
